iT邦幫忙

2023 iThome 鐵人賽

DAY 27
0
Vue.js

試試用Vue建立網站吧系列 第 27

Day27-試試Vue3-餐廳實評頁面(2)

  • 分享至 

  • xImage
  •  

程式碼盡量只寫要說明的地方,無關的就不贅述。

(3-4)頁面表單提交的規範

  • 點選任一張卡片時,表單中「餐廳評論」欄位才可進行編輯。否則唯讀狀態。
    • 原本已有評論的卡片,點選後該欄位會顯示其評論。僅供編輯不可移除評論。
  • 表單中「餐廳評論」欄位必須有值才可提交表單。否則跳出「餐廳評論欄位不可空值!」的警告訊息。
  • 表單提交後讓所有欄位重置成預設值。

(3-5)頁面上面區塊(口袋餐廳表單)
該區塊的操作情境請參考之前的「Day25-試試Vue3-規劃餐廳實評的頁面」篇。

  • 點選任一張卡片觸發 selectCard(item) 函式

    • 前一篇「Day26-試試Vue3-餐廳實評頁面(1)」有提到函式會將卡片資料(即 item )依序賦值給 selectCardList 裡的相應屬性。
    • 表單中「品牌受眾」、「品牌名稱」、「地址」欄位的預設值就能取自 selectCardList 中的 cardBrandName 、 cardType 、 cardAddress 。
    • 「餐廳評論」欄位因為觸發 selectCard(item) 函式而被允許會員操作。所以標籤 <textarea></textarea> 加上 v-model="selectCardList.cardComment" 可以將會員輸入的評論作為內容值,去變更 data 裡的屬性 electCardList.cardComment 。
  • 當表單按鈕「提交評論」被操作時,會觸發方法裡的 submitComment() 函式

    • 禁止按鈕提交的默認(如頁面刷新)行為,使用 event.preventDefault(); 處理。
    • 先判斷「餐廳評論」欄位是否填妥,若有才進行以下動作。若否則跳出警告訊息。
      • 使用 axios.get() 取得會員廳餐 API 資料,回傳的資料為一陣列,取裡面的物件賦值給變數 userData 。
      • 透過 findIndex 方法找出會員所選的這張卡片資料,在會員廳餐 API 裡是第幾筆(即找出其索引值)。
      • 會員餐廳 API 裡沒有屬性 comment ,需要用物件取值的括弧記法在屬性 userRestaurants 裡面新增屬性並為它賦值。
      • 將處理完的資料寫回會員餐廳 API 並用 axios.put() 進行網路請求。
        • 不管網路請求成功還是失敗,表單裡所有欄位內容都必須恢復預設值。在此使用 axios 裡的 .finally() 去執行(適用在不論請求成功或失敗都會執行)。如果把重置表單語法寫在 submitComment() 底部會發生非同步情況。因為該次 axios 網路請求時間較長,系統會把它安排到佇列清單裡等待,接續執行後面的語法(含重置表單),待佇列清單裡的項目有回應時才回頭執行。這邊的 axios 網路請求回應後執行 findIndex 會跳錯誤訊息,表示無法找到該筆資料。因為表單參數 selectCardList.cardBrandName 被重置成空字串,導致 findIndex 中無法再它來比對 API 裡的資料。
      • 最後執行 userPocket() 函式使頁面上的口袋餐廳資料能夠即時呈現。
<template>
  <div class="container">
    ...
    <section class="row mb-4">
      <div class="col-4">
        <h1>餐廳實評</h1>
        <img
          src="https://plus.unsplash.com/premium_photo-1671493286864-f354f3d2feb5?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1974&q=80"
          alt="關於喜愛的圖"
          class="img-fluid"
        />
      </div>
      <div class="col"></div>
      <div class="col-4 mt-5">
        <form>
          <div class="row g-2 mb-4">
            <label for="type" class="col-sm-3 col-form-label">品牌受眾</label>
            <input
              type="text"
              id="type"
              class="form-control col-sm"
              :placeholder="selectCardList.cardType"
              disabled
            />
          </div>
          <div class="row g-2 mb-4">
            <label for="res-name" class="col-sm-3 col-form-label"
              >品牌名稱</label>
            <input
              type="text"
              id="res-name"
              class="form-control col-sm"
              :placeholder="selectCardList.cardBrandName"
              disabled
            />
          </div>
          <div class="row g-2 mb-4">
            <label for="address" class="col-sm-3 col-form-label">地址</label>
            <input
              type="text"
              id="address"
              class="form-control col-sm"
              :placeholder="selectCardList.cardAddress"
              disabled
            />
          </div>
          <div class="row g-2 mb-4">
            <label
              for="exampleFormControlTextarea1"
              class="col-sm-3 col-form-label"
              >餐廳評論</label>
            <textarea
              class="form-control col-sm"
              id="exampleFormControlTextarea1"
              rows="6"
              :disabled="commentDisabled"
              v-model="selectCardList.cardComment"
            ></textarea>
          </div>
          <div class="d-flex justify-content-end">
            <button
              class="btn btn-lg btn-primary mb-4"
              type="submit"
              @click="submitComment()">
              提交評論
            </button>
          </div>
        </form>
      </div>
    </section>
    ...
  </div>
</template>
<script>
import axios from "axios";
import { mapState } from "pinia";

// 定義好的 store 賦值給變數 useLoginStore
// 在元件中引入並呼叫 useLoginStore() 來訪問 store
import { useLoginStore } from "../../components/LoginStore.js";

export default {
  data() {
    return {
      userResList: [],
      selectCardList: {
        cardBrandName: "",
        cardType: "",
        cardAddress: "",
        cardComment: "",
      },
      commentDisabled: true,

      apiUserResUrl: "",
      apiUserResIdUrl: "",
    };
  },
  methods: {
    userPocket() {
      axios
        .get(this.apiUserResUrl)
        .then((res) => {
          console.log(res);
          this.userResList = res.data[0].userRestaurants;
        })
        .catch((error) => {
          console.log(error);
        });
    },
    selectCard(item) {
      ...
    },
    submitComment() {
      event.preventDefault(); // 禁止按鈕提交的默認行為

      // 餐廳評論欄位必須有值才可提交表單
      if (this.selectCardList.cardComment !== "") {
        axios
          .get(this.apiUserResUrl)
          .then((res) => {
            console.log(res);

            // res.data是陣列要變成物件後面才能用push,所以取第一個內容(這裡是物件)
            const userData = res.data[0];
            console.log(userData);

            const dataIndex = userData.userRestaurants.findIndex(
              (dataIndex) =>
                dataIndex.brandName === this.selectCardList.cardBrandName
            );
            console.log(dataIndex);

            userData.userRestaurants[dataIndex]["comment"] =
              this.selectCardList.cardComment;

            axios
              .put(this.apiUserResIdUrl, userData)
              .then((res) => {
                console.log(res);
                alert("該餐廳評論已更新");
                this.userPocket();
              })
              .catch((error) => {
                console.log(error);
                alert("餐廳評論更新失敗");
              });
          })
          .catch((error) => {
            console.log(error);
            alert("無法更新到會員口袋餐廳裡,請洽網站管理員!");
          })
          .finally(() => {
            // 不論請求成功或失敗都会執行
            this.selectCardList.cardBrandName = "";
            this.selectCardList.cardType = "";
            this.selectCardList.cardAddress = "";
            this.selectCardList.cardComment = "";
            // 表單提交後讓關閉對餐廳評論的操作
            this.commentDisabled = true;
          });
      } else {
        alert("餐廳評欄位不可空值!");
      }
    },
  },
  created() {
    this.apiUserResUrl = `http://localhost:3002/userRes?email=${this.userEmail}`;
    this.apiUserResIdUrl = `http://localhost:3002/userRes/${this.userId}`;
  },
  mounted() {
    this.userPocket();
  },
  // 監聽data
  computed: {
    ...mapState(useLoginStore, {
      // 'name' 是 store 中的狀態名,'userName' 是在組件中的名稱
      userName: (state) => state.name,
      userEmail: (state) => state.email,
      userId: (state) => state.id,
    }),
  },
};
</script>

上一篇
Day26-試試Vue3-餐廳實評頁面(1)
下一篇
Day28-試試Vue3-後台管理者頁面(1)
系列文
試試用Vue建立網站吧30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言